﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;

namespace iNet.Collections
{
	/// <summary>
	/// 由多個子列表所組成的單一列表。
	/// </summary>
	public class CompoundList<T> : IList, IList<T>, INotifyCollectionChanged
	{
		#region Enumerator
		class Enumerator : IEnumerator<T>
		{
			#region 欄位
			IEnumerator<T> _CurrentEnumerator;
			int _CurrentSubListIndex = -1;
			readonly CompoundList<T> _Owner;
			readonly int _Version;
			#endregion
			#region 建構子
			public Enumerator(CompoundList<T> owner)
			{
				_Owner = owner;
				_Version = owner._Version;
			}
			#endregion
			#region Current
			public T Current
			{
				get
				{
					if (_CurrentEnumerator != null)
						return _CurrentEnumerator.Current;
					throw new InvalidOperationException();
				}
			}
			#endregion
			#region Dispose
			public void Dispose()
			{
				if (_CurrentEnumerator != null)
				{
					_CurrentEnumerator.Dispose();
					_CurrentEnumerator = null;
				}
				_CurrentSubListIndex = _Owner._SourceLists.Count;
			}
			#endregion
			#region  IEnumerator.Current
			object IEnumerator.Current
			{
				get
				{
					return this.Current;
				}
			}
			#endregion
			#region MoveNext
			public bool MoveNext()
			{
				//確認版本
				if (_Owner._Version != _Version)
					throw new InvalidOperationException();

				//移動下一個元素
				while (true)
				{
					//移動至子列表中的下一個元素
					if (_CurrentEnumerator != null)
					{
						if (_CurrentEnumerator.MoveNext())
							return true;
						_CurrentEnumerator.Dispose();
						_CurrentEnumerator = null;
					}

					//移動至下一個子列表
					if (_CurrentSubListIndex >= (_Owner._SourceLists.Count - 1))
						return false;
					++_CurrentSubListIndex;
					_CurrentEnumerator = _Owner._SourceLists[_CurrentSubListIndex].GetEnumerator();
				}
			}
			#endregion
			#region Reset
			public void Reset()
			{
				if (_CurrentEnumerator != null)
				{
					_CurrentEnumerator.Dispose();
					_CurrentEnumerator = null;
				}
				_CurrentSubListIndex = -1;
			}
			#endregion
		}
		#endregion
		#region SourceListCollection
		class SourceListCollection : IList<IList<T>>
		{
			#region 欄位
			readonly List<IList<T>> _List = new List<IList<T>>();
			readonly CompoundList<T> _Owner;
			#endregion
			#region 建構子
			public SourceListCollection(CompoundList<T> owner)
			{
				_Owner = owner;
			}
			#endregion
			#region Add
			public void Add(IList<T> item)
			{
				this.Insert(_List.Count, item);
			}
			#endregion
			#region Clear
			public void Clear()
			{
				for (int i = _List.Count; i >= 0; --i)
					this.StopMonitoringSourceList(_List[i]);
				_Owner.OnSourceListCleared();
			}
			#endregion
			#region Contains
			public bool Contains(IList<T> item)
			{
				return _List.Contains(item);
			}
			#endregion
			#region CopyTo
			public void CopyTo(IList<T>[] array, int arrayIndex)
			{
				_List.CopyTo(array, arrayIndex);
			}
			#endregion
			#region Count
			public int Count
			{
				get
				{
					return _List.Count;
				}
			}
			#endregion
			#region GetEnumerator
			public IEnumerator<IList<T>> GetEnumerator()
			{
				return _List.GetEnumerator();
			}
			#endregion
			#region IEnumerable.GetEnumerator
			IEnumerator IEnumerable.GetEnumerator()
			{
				return this.GetEnumerator();
			}
			#endregion
			#region IndexOf
			public int IndexOf(IList<T> item)
			{
				return _List.IndexOf(item);
			}
			#endregion
			#region Insert
			public void Insert(int index, IList<T> item)
			{
				if (item == null)
					throw new ArgumentNullException("item");
				_List.Insert(index, item);
				this.StartMonitoringSourceList(item);
				_Owner.OnSourceListInserted(index, item);
			}
			#endregion
			#region IsReadOnly
			public bool IsReadOnly
			{
				get
				{
					return false;
				}
			}
			#endregion
			#region OnSourceListChanged
			void OnSourceListChanged(object sender, NotifyCollectionChangedEventArgs e)
			{
				_Owner.OnSourceListChanged(sender as IList<T>, e);
			}
			#endregion
			#region Remove
			public bool Remove(IList<T> item)
			{
				if (item == null)
					return false;
				int index = _List.IndexOf(item);
				if (index >= 0)
				{
					this.RemoveAt(index);
					return true;
				}
				return false;
			}
			#endregion
			#region RemoveAt
			public void RemoveAt(int index)
			{
				if (index < 0 || index >= this.Count)
					throw new ArgumentOutOfRangeException("index");
				IList<T> list = _List[index];
				_List.RemoveAt(index);
				this.StopMonitoringSourceList(list);
				_Owner.OnSourceListRemoved(index, list);
			}
			#endregion
			#region StartMonitoringSourceList
			void StartMonitoringSourceList(IList<T> list)
			{
				INotifyCollectionChanged eventSource = (list as INotifyCollectionChanged);
				if (eventSource != null)
					eventSource.CollectionChanged += this.OnSourceListChanged;
			}
			#endregion
			#region StopMonitoringSourceList
			void StopMonitoringSourceList(IList<T> list)
			{
				INotifyCollectionChanged eventSource = (list as INotifyCollectionChanged);
				if (eventSource != null)
					eventSource.CollectionChanged -= this.OnSourceListChanged;
			}
			#endregion
			#region this[index]
			public IList<T> this[int index]
			{
				get
				{
					return _List[index];
				}
				set
				{
					if (value == null)
						throw new ArgumentNullException("value");
					IList<T> oldList = _List[index];
					if (oldList != value)
					{
						this.StopMonitoringSourceList(oldList);
						this.StartMonitoringSourceList(value);
						_List[index] = value;
						_Owner.OnSourceListRemoved(index, oldList);
						_Owner.OnSourceListInserted(index, value);
					}
				}
			}
			#endregion
		}
		#endregion
		#region 欄位
		readonly SourceListCollection _SourceLists;
		int _Version;
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 CompoundList 執行個體。
		/// </summary>
		public CompoundList()
		{
			_SourceLists = new SourceListCollection(this);
		}
		#endregion
		#region CollectionChanged
		/// <summary>
		/// 發生在集合內容改變後。
		/// </summary>
		public event NotifyCollectionChangedEventHandler CollectionChanged;
		#endregion
		#region Contains
		/// <summary>
		/// 確認列表中是否包含指定的元素。
		/// </summary>
		/// <param name="item">指定的元素。</param>
		/// <returns>True 表示列表中包含指定的元素，False 表示不包含。</returns>
		public bool Contains(T item)
		{
			for (int i = _SourceLists.Count - 1; i >= 0; --i)
			{
				IList<T> list = _SourceLists[i];
				if (list.Contains(item))
					return true;
			}
			return false;
		}
		#endregion
		#region CopyTo
		/// <summary>
		/// 將列表中的元素值或參考複製至現有的一維陣列中。
		/// </summary>
		/// <param name="array">現有的一維陣列。</param>
		/// <param name="arrayIndex">複製的陣列起始索引值，複製的資料將從這裡開始擺放。</param>
		/// <exception cref="ArgumentNullException">array 為 Null (Visual Basic 為 Nothing)。</exception>
		/// <exception cref="ArgumentOutOfRangeException">arrayIndex 超出範圍。</exception>
		public void CopyTo(T[] array, int arrayIndex)
		{
			//確認參數
			if (array == null)
				throw new ArgumentNullException("array");
			int count = this.Count;
			if (arrayIndex < 0 || (arrayIndex + count) > array.Length)
				throw new ArgumentOutOfRangeException("arrayIndex");

			//複製參考
			for (int i = 0, listCount = _SourceLists.Count; i < listCount; ++i)
			{
				IList<T> list = _SourceLists[i];
				int itemCount = list.Count;
				if (itemCount > 0)
				{
					list.CopyTo(array, arrayIndex);
					arrayIndex += itemCount;
				}
			}
		}
		#endregion
		#region Count
		/// <summary>
		/// 取得元素個數。
		/// </summary>
		public int Count
		{
			get
			{
				int count = 0;
				for (int i = _SourceLists.Count - 1; i >= 0; --i)
					count += _SourceLists[i].Count;
				return count;
			}
		}
		#endregion
		#region GetEnumerator
		/// <summary>
		/// 取得列舉元素的 IEnumerator&lt;T&gt; 介面。
		/// </summary>
		/// <returns>IEnumerator&lt;T&gt; 介面。</returns>
		public IEnumerator<T> GetEnumerator()
		{
			return new Enumerator(this);
		}
		#endregion
		#region GetItemIndex
		int GetItemIndex(int subListIndex, int indexInSubList)
		{
			int index = 0;
			for (int i = subListIndex - 1; i >= 0; --i)
			{
				IList<T> subList = _SourceLists[i];
				index += subList.Count;
			}
			return (index + indexInSubList);
		}
		#endregion
		#region ICollection.CopyTo
		void ICollection.CopyTo(Array array, int index)
		{
			//確認參數
			if (array == null)
				throw new ArgumentNullException("array");
			if (array.Rank != 1)
				throw new ArgumentException("Rank of array must be 1");
			int count = this.Count;
			if (index < 0 || (index + count) > array.Length)
				throw new ArgumentOutOfRangeException("index");

			//確認陣列型別
			Type arrayElementType = array.GetType().GetElementType();
			if (!arrayElementType.IsAssignableFrom(typeof(T)))
				throw new ArgumentException("Array element type is incompatible");

			//複製參考
			for (int i = 0, listCount = _SourceLists.Count; i < listCount; ++i)
			{
				IList<T> list = _SourceLists[i];
				int itemCount = list.Count;
				for (int j = 0; j < itemCount; ++j)
					array.SetValue(list[j], index++);
			}
		}
		#endregion
		#region ICollection.IsSynchronized
		bool ICollection.IsSynchronized
		{
			get
			{
				return false;
			}
		}
		#endregion
		#region ICollection.SyncRoot
		object ICollection.SyncRoot
		{
			get
			{
				return this;
			}
		}
		#endregion
		#region ICollection<T>.Add
		void ICollection<T>.Add(T item)
		{
			throw new InvalidOperationException();
		}
		#endregion
		#region ICollection<T>.Clear
		void ICollection<T>.Clear()
		{
			throw new InvalidOperationException();
		}
		#endregion
		#region ICollection<T>.IsReadOnly
		bool ICollection<T>.IsReadOnly
		{
			get
			{
				return true;
			}
		}
		#endregion
		#region ICollection<T>.Remove
		bool ICollection<T>.Remove(T item)
		{
			throw new InvalidOperationException();
		}
		#endregion
		#region IEnumerable.GetEnumerator
		IEnumerator IEnumerable.GetEnumerator()
		{
			return this.GetEnumerator();
		}
		#endregion
		#region IList.Add
		int IList.Add(object value)
		{
			throw new InvalidOperationException();
		}
		#endregion
		#region IList.Clear
		void IList.Clear()
		{
			throw new InvalidOperationException();
		}
		#endregion
		#region IList.Contains
		bool IList.Contains(object value)
		{
			if (value is T)
				return this.Contains((T)value);
			return false;
		}
		#endregion
		#region IList.IndexOf
		int IList.IndexOf(object value)
		{
			if (value is T)
				return this.IndexOf((T)value);
			return -1;
		}
		#endregion
		#region IList.Insert
		void IList.Insert(int index, object value)
		{
			throw new InvalidOperationException();
		}
		#endregion
		#region IList.IsFixedSize
		bool IList.IsFixedSize
		{
			get
			{
				return false;
			}
		}
		#endregion
		#region IList.IsReadOnly
		bool IList.IsReadOnly
		{
			get
			{
				return true;
			}
		}
		#endregion
		#region IList.Remove
		void IList.Remove(object value)
		{
			throw new InvalidOperationException();
		}
		#endregion
		#region IList.RemoveAt
		void IList.RemoveAt(int index)
		{
			throw new InvalidOperationException();
		}
		#endregion
		#region IList.this[index]
		object IList.this[int index]
		{
			get
			{
				return this[index];
			}
			set
			{
				throw new InvalidOperationException();
			}
		}
		#endregion
		#region IList<T>.Insert
		void IList<T>.Insert(int index, T item)
		{
			throw new InvalidOperationException();
		}
		#endregion
		#region IList<T>.RemoveAt
		void IList<T>.RemoveAt(int index)
		{
			throw new InvalidOperationException();
		}
		#endregion
		#region IList<T>.this[index]
		T IList<T>.this[int index]
		{
			get
			{
				return this[index];
			}
			set
			{
				throw new InvalidOperationException();
			}
		}
		#endregion
		#region IndexOf
		/// <summary>
		/// 取得指定元素在列表中的索引位置。
		/// </summary>
		/// <param name="item">指定的元素。</param>
		/// <returns>元素在列表中的索引位置，-1 表示該元素不在此列表中。</returns>
		public int IndexOf(T item)
		{
			int baseIndex = 0;
			for (int i = 0, count = _SourceLists.Count; i < count; ++i)
			{
				IList<T> list = _SourceLists[i];
				int index = list.IndexOf(item);
				if (index >= 0)
					return (baseIndex + index);
				baseIndex += list.Count;
			}
			return -1;
		}
		#endregion
		#region OnCollectionChanged
		void OnCollectionChanged(NotifyCollectionChangedAction action, IList<T> items, int startIndex)
		{
			if (this.CollectionChanged != null)
			{
				//轉換集合
				IList list = (items as IList);
				if (list == null && items != null)
				{
					int count = items.Count;
					T[] array = new T[count];
					for (int i = count - 1; i >= 0; --i)
						array[i] = items[i];
					list = array;
				}

				//引發事件
				this.OnCollectionChanged(action, list, startIndex);
			}
		}
		void OnCollectionChanged(NotifyCollectionChangedAction action, IList items, int startIndex)
		{
			if (this.CollectionChanged != null)
			{
				switch (action)
				{
					case NotifyCollectionChangedAction.Add:
						if (items != null && items.Count > 1)
						{
							for (int i = 0, count = items.Count; i < count; ++i)
							{
								object item = items[i];
								this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, item, startIndex + i));
							}
						}
						else
							this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, items, startIndex));
						break;
					case NotifyCollectionChangedAction.Remove:
						if (items != null && items.Count > 1)
						{
							for (int i = items.Count - 1; i >= 0; --i)
							{
								object item = items[i];
								this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, item, startIndex + i));
							}
						}
						else
							this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, items, startIndex));
						break;
					case NotifyCollectionChangedAction.Reset:
						this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(action));
						break;
				}
			}
		}
		void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
		{
			if (this.CollectionChanged != null)
				this.CollectionChanged(this, e);
		}
		#endregion
		#region OnSourceListChanged
		void OnSourceListChanged(IList<T> list, NotifyCollectionChangedEventArgs e)
		{
			int listIndex = _SourceLists.IndexOf(list);
			switch (e.Action)
			{
				case NotifyCollectionChangedAction.Add:
					{
						int startIndex = this.GetItemIndex(listIndex, e.NewStartingIndex);
						this.OnCollectionChanged(NotifyCollectionChangedAction.Add, e.NewItems, startIndex);
					}
					break;
				case NotifyCollectionChangedAction.Move:
					{
						int newStartIndex = this.GetItemIndex(listIndex, e.NewStartingIndex);
						int oldStartIndex = this.GetItemIndex(listIndex, e.OldStartingIndex);
						this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, e.OldItems, newStartIndex, oldStartIndex));
					}
					break;
				case NotifyCollectionChangedAction.Remove:
					{
						int startIndex = this.GetItemIndex(listIndex, e.OldStartingIndex);
						this.OnCollectionChanged(NotifyCollectionChangedAction.Remove, e.OldItems, startIndex);
					}
					break;
				case NotifyCollectionChangedAction.Replace:
					{
						int startIndex = this.GetItemIndex(listIndex, e.OldStartingIndex);
						this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, e.NewItems, e.OldItems, startIndex));
					}
					break;
				case NotifyCollectionChangedAction.Reset:
					this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
					break;
			}
		}
		#endregion
		#region OnSourceListCleared
		void OnSourceListCleared()
		{
			++_Version;
			this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
		}
		#endregion
		#region OnSourceListInserted
		void OnSourceListInserted(int index, IList<T> list)
		{
			++_Version;
			if (list.Count > 0)
				this.OnCollectionChanged(NotifyCollectionChangedAction.Add, list, this.GetItemIndex(index, 0));
		}
		#endregion
		#region OnSourceListRemoved
		void OnSourceListRemoved(int index, IList<T> list)
		{
			++_Version;
			if (list.Count > 0)
				this.OnCollectionChanged(NotifyCollectionChangedAction.Remove, list, this.GetItemIndex(index, 0));
		}
		#endregion
		#region SourceLists
		/// <summary>
		/// 取得來源列表集合。
		/// </summary>
		public IList<IList<T>> SourceLists
		{
			get
			{
				return _SourceLists;
			}
		}
		#endregion
		#region this[index]
		/// <summary>
		/// 取得位於指定索引位置的元素。
		/// </summary>
		/// <param name="index">指定的索引位置。</param>
		/// <returns>位於指定索引位置的元素。</returns>
		public T this[int index]
		{
			get
			{
				if (index >= 0)
				{
					for (int i = 0, count = _SourceLists.Count; i < count; ++i)
					{
						IList<T> list = _SourceLists[i];
						int itemCount = list.Count;
						if (index < itemCount)
							return list[index];
						index -= itemCount;
					}
				}
				throw new IndexOutOfRangeException();
			}
		}
		#endregion
	}
}
